package com.access.controller;

import Jama.Matrix;
import com.access.db.PolicyCacheManager;
import access.db.SQLServer;
import access.model.AccessPolicy;
import access.model.Environment;
import access.model.Object;
import access.model.Subject;

import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * Created by koujx on 2016/4/21.
 */
public class AccessController {
    public final static String WLAN_IPRANGE_REGEX = "10.8.(0|2[0-4]{0,1}[0-9]{0,1}|25[0-5]|[1][0-9]{0,2}|[3-9]{1}[0-9]{0,1}).(0|2[0-4]{0,1}[0-9]{0,1}|25[0-5]|[1][0-9]{0,2}|[3-9]{1}[0-9]{0,1})";

    //api访问控制
    public boolean apiAccessControl(int userId, String apiName, HttpServletRequest request) {
        try {
            AccessPolicy accessPolicy = PolicyCacheManager.getAccessPolicy(apiName);
            Subject sub = SQLServer.queryUser(userId);
            if (ac(sub, accessPolicy, request)) return true;
            else return false;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
    //数据访问控制
    public boolean accessControl(int userId, String dataId, String operation, HttpServletRequest request) {
        try {
            long t1=System.currentTimeMillis();

            AccessPolicy accessPolicy = PolicyCacheManager.getAccessPolicy(dataId, operation);
            Subject sub = SQLServer.queryUser(userId);
            long t2=System.currentTimeMillis();
            long t=t2-t1;
            System.out.println(t);
            return ac(sub, accessPolicy, request);

        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean ac(Subject sub,AccessPolicy accessPolicy,HttpServletRequest request){
        Object obj = new Object();
        Environment env = new Environment();
        DateFormat format = new SimpleDateFormat("HH");
        Date date = new Date();
        env.setTime(format.format(date));
        env.setIp(request.getRemoteAddr());
        if (Pattern.matches(WLAN_IPRANGE_REGEX, request.getRemoteAddr())) {
            env.setMobile(true);
        } else {
            env.setMobile(false);
        }
        env = envCompute(env, accessPolicy.getAttributes());
        String accessAttributes = sub.subToString() + "," + env.envToString();
        return compute(accessAttributes, accessPolicy);
    }

    public boolean chainAccessControl(int userId, String dataId, String operation, HttpServletRequest request) {

        try {
            long t1=System.currentTimeMillis();

            Subject sub = SQLServer.queryUser(userId);
            String accessAttributes = sub.subToString();
            AccessPolicy accessPolicy = SQLServer.getAccessPolicy(dataId, operation);
            String attributes = accessPolicy.getAttributes().split("#")[0];
            String vectors = accessPolicy.getVectors().split("#")[0];
            AccessPolicy ap = new AccessPolicy();
            ap.setAttributes(attributes);
            ap.setVectors(vectors);
            ap.setLengthOfVector(accessPolicy.getLengthOfVector());
            long t2=System.currentTimeMillis();
            long t=t2-t1;
            System.out.println(t);
            if (ap.getAttributes().equals("")) return true;
            else return access(accessAttributes, ap);

        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }

    }

    public boolean compute(String accessAttributes, AccessPolicy accessPolicy) {
        if (accessPolicy == null) return true;
        else if (accessAttributes == null || accessAttributes.length() <= 0) {
            return false;
        } else {
            boolean result = true;
            String[] attributes = accessPolicy.getAttributes().split("#");
            String[] vectors = accessPolicy.getVectors().split("#");
            for (int i = 0; i < attributes.length; i++) {
                AccessPolicy ap = new AccessPolicy();
                ap.setAttributes(attributes[i]);
                ap.setVectors(vectors[i]);
                ap.setLengthOfVector(accessPolicy.getLengthOfVector());
                result = result && access(accessAttributes, ap);
            }
            return result;
        }
    }

    public boolean access(String accessAttributes, AccessPolicy accessPolicy) {
        //属性矩阵
        long t1=System.currentTimeMillis();

        Map<String, String> policyMap = new HashMap<>();
        StringTokenizer tokenOfVector = new StringTokenizer(accessPolicy.getVectors(), ";");
        String[] vectors = new String[tokenOfVector.countTokens()];
        int num = 0;
        while (tokenOfVector.hasMoreElements()) {
            String vector = tokenOfVector.nextToken();
            vectors[num] = vector;
            num++;
        }
        StringTokenizer tokenOfAPolicy = new StringTokenizer(accessPolicy.getAttributes(), ",");
        String[] attributes = new String[tokenOfAPolicy.countTokens()];
        int num1 = 0;
        while (tokenOfAPolicy.hasMoreElements()) {
            String attribute = tokenOfAPolicy.nextToken();
            attributes[num1] = attribute;
            num1++;
        }
        for (int i = 0; i < attributes.length; i++) {
            policyMap.put(attributes[i], vectors[i]);
        }

        //子属性列表
        StringTokenizer tokenOfAParams = new StringTokenizer(accessAttributes, ",");
        String[] aparams = new String[tokenOfAParams.countTokens()];
        int num2 = 0;
        while (tokenOfAParams.hasMoreElements()) {
            String param = tokenOfAParams.nextToken();
            aparams[num2] = param;
            num2++;
        }


        //子属性矩阵
        Map<String, Boolean> map = new HashMap<>();
        for (String str : attributes) {
            map.put(str, Boolean.FALSE);
        }
        for (String str : aparams) {
            if (map.containsKey(str)) {
                map.put(str, Boolean.TRUE);
            }
        }
        //System.out.println("子属性集："+aparams.length);

        LinkedList<String> subAttrList = new LinkedList<String>();
        for (Map.Entry<String, Boolean> e : map.entrySet()) {
            if (e.getValue().equals(Boolean.TRUE)) {
                subAttrList.add(e.getKey());
            }
        }
        int numSubAttrs = subAttrList.size();
        System.out.println("子属性集内有" + numSubAttrs + "个属性:" + subAttrList.toString());


        long t2=System.currentTimeMillis();
        long t=t2-t1;
        System.out.println(t);


        if (numSubAttrs == 0) return false;
        else return attributeMatrix(subAttrList, policyMap, numSubAttrs, accessPolicy.getLengthOfVector());

    }


    public boolean attributeMatrix(LinkedList attrs, Map<String, String> map, int row, int column) {
        double[][] vectors = new double[row + 1][column];
        for (int i = 0; i < row; i++) {
            String attr = (String) attrs.get(i);
            if (map.containsKey(attr)) {
                String[] vector = map.get(attr).split(",");
                for (int j = 0; j < vector.length; j++) {
                    vectors[i][j] = Double.valueOf(vector[j]);
                }
            }
        }
        vectors[row][0] = 1;
        Matrix ma = new Matrix(vectors).transpose();
        Matrix m = ma.getMatrix(0, column - 1, 0, row - 1);

//        System.out.println("属性子矩阵转置矩阵：");
//        m.print(2, 0);
//        System.out.println("子矩阵转置矩阵的秩:" + m.rank());
//        System.out.println("线性方程组的增广矩阵：");
//        ma.print(2, 0);
//        System.out.println("增广的秩:" + ma.rank());

        if (ma.rank() > m.rank()) return false;
        else return true;
    }

    private Environment envCompute(Environment env, String attributes) {
        String[] policies = attributes.split("#");
        for (String attrs : policies) {
            String[] attr = attrs.split(",");
            for (String str : attr) {
                if (str.indexOf("-") >= 0) {
                    if (str.length() <= 5) {
                        String[] time = str.split("-");
                        int h = Integer.parseInt(env.getTime());
                        if (h >= Integer.parseInt(time[0]) && h <= Integer.parseInt(time[1])) {
                            env.setTime(str);
                        }
                    } else {
                        if (Pattern.matches(str, env.getIp())) env.setIp(str);
                    }
                }
            }
        }
        return env;
    }
}
